#include "LexAnalysis.hpp"

LexAnalysis::~LexAnalysis()
{
    /* close files */
    ifs_source.close();
    ofs_result.close();
    ofs_error.close();
}

void LexAnalysis::lineScanner(std::filesystem::path sourceFile)
{
    std::cout << "*** Lexical analysis ***" << std::endl
              << " - Source file: " << sourceFile.filename()
              << std::endl;

    loadData(sourceFile);

    /* init data */
    token_number = 0;
    line_number = 0;
    error_number = 0;

    /* line scanner */
    int i;
    while (std::getline(ifs_source, lineString))
    {
        line_number++;

        currentChar = lineString.begin();
        while (std::isspace(*currentChar))
            currentChar++;

        for (i = 0; currentChar != lineString.end() && i < lineString.length(); i++)
        {
            token = tokenizer.getNextToken(currentChar);
            outputToken();

            /* update currentChar */
            currentChar += token.value.length();

            while (std::isspace(*currentChar))
                currentChar++;
        }

        /* end of line */
        token.state = END_LINE;
        outputToken();
    }
    /* end of file */
    token.state = END_FILE;
    outputToken();

    /* output scanning result */
    std::cout << "*** File lexical analysis completed! ***" << std::endl
              << error_number << " error(s) found in "
              << token_number << " tokens"
              << std::endl;

    /* close files */
    ifs_source.close();
    ofs_result.close();
    ofs_error.close();
}

void LexAnalysis::loadData(std::filesystem::path sourceFile)
{
    /* sourceFile path validity */
    if (!std::filesystem::exists(sourceFile))
    {
        std::cerr << "source file path invalid!!!" << std::endl;
        std::exit(EXIT_FAILURE);
    }

    /* open sourceFile */
    ifs_source.open(sourceFile, std::ios::in);
    if (!ifs_source.is_open())
    {
        std::cerr << sourceFile.string() + " open failed!!!" << std::endl;
        std::exit(EXIT_FAILURE);
    }

    /* create .dyd & .err */
    ofs_result.open(sourceFile.replace_extension(".dyd"), std::ios::trunc);
    ofs_error.open(sourceFile.replace_extension(".err"), std::ios::trunc);
}

void LexAnalysis::outputToken()
{
    token_number++;

    switch (token.state)
    {
    /* save token to file */
    case ACCEPT:
        ofs_result << std::setw(16) << std::setfill(' ') << std::right << token.value
                   << ' '
                   << std::setw(2) << std::setfill('0') << token.type
                   << std::endl;
        break;

    case END_LINE:
        ofs_result << "            EOLN 24" << std::endl;
        break;

    case END_FILE:
        ofs_result << "             EOF 25" << std::endl;
        break;

    /* error manage */
    case ERROR_ILLEGALCHAR:
        formateErrorOutput("非法字符");
        break;

    case ERROR_COLON_MATCH:
        formateErrorOutput("冒号不匹配");
        break;

    case ERROR_IDENTIFIER_OVERFLOW:
        formateErrorOutput("标识符长度溢出");
        break;

    case ERROR_CONSTANT_OVERFLOW:
        formateErrorOutput("常量长度溢出");
        break;

    case OTHER:
        formateErrorOutput("未知错误");
        break;

    default:
        break;
    }
}

void LexAnalysis::formateErrorOutput(std::string error_info)
{
    error_number++;

    ofs_error << "***" << line_number << ":  " << error_info << std::endl;

#ifdef ERRORINFO_STDERR
    int row_position = (currentChar - lineString.begin());
    std::string under_line;
    int i;
    for (i = 0; i < row_position; i++)
        under_line.push_back(' ');
    for (i = 0; i < token.value.length(); i++)
        under_line.push_back('^');

    std::cerr << "\033[1;31m笨蛋！\033[0m 就...就在那个..."
              << "LINE:" << line_number
              << "  \033[1;34m" << error_info << "\033[0m"
              << " " << token.info << std::endl
              << lineString << std::endl
              << "\033[1;33m" << under_line << "\033[0m"
              << std::endl;
#endif
}